package org.springboot.file.controller;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 下载-支持断点续传
 * 
 * @author lisy
 */
@RestController
@RequestMapping("/download")
public class DownloadController {
	
	static Logger logger = LoggerFactory.getLogger(DownloadController.class);
	
	@Value("${file.dir}")
	String dir;
	@Value("${file.download.url}")
	String downloadUrl;
	
	public static final String READ_FILE_NAME = "test.txt";
	public static final String WRITE_FILE_NAME = "test-download.txt";
	
	RestTemplate restTemplate = new RestTemplate();
	ObjectMapper objectMapper = new ObjectMapper();
	
	@RequestMapping(value = "/file", method = RequestMethod.GET)
    public String file() {
		long pos = 0;
		while (true) {
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
			MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
			HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<MultiValueMap<String, Object>>(paramMap, headers);
			paramMap.add("pos", String.valueOf(pos));
			ResponseEntity<String> resp = restTemplate.postForEntity(downloadUrl, request, String.class);
			String body = resp.getBody();
			if (body == null || "".equals(body)) {
				return "未获取到文件信息!";
			}
			try {
				@SuppressWarnings("unchecked")
				Map<String, String>	resMap = objectMapper.readValue(body, Map.class);
				if (!"success".equalsIgnoreCase(resMap.get("status"))) {
					return "文件下载失败："+resMap.get("msg");
				}
				long size = Long.valueOf(resMap.get("size"));
				String files = resMap.get("files");
				byte[] file = Base64.getDecoder().decode(files);
				// 云下程序把数据追加到文件
				File targetFile = new File(dir + File.separator  + WRITE_FILE_NAME);
				RandomAccessFile writeFile = null;
				try {
					writeFile = new RandomAccessFile(targetFile, "rw");
					writeFile.seek(pos);
					// 数据写入
					writeFile.write(file);
					writeFile.close();
				} catch (IOException e) {
					logger.error("文件下载失败：" + String.valueOf(body) + e);
					return "文件下载失败：" + String.valueOf(body);
				} finally {
					try {
						// 关闭输出流
						if (writeFile != null)
							writeFile.close();
					} catch (IOException e) {
						logger.error("文件流关闭异常！" + e);
						return "文件流关闭异常：" + String.valueOf(body);
					}
				}
				// 实际应用可以保存到数据库，做到断点续传
				pos = targetFile.length();
				if (size == targetFile.length()) {
					logger.info("完成文件下载!");
					return "下载成功！";
				}
				logger.info("文件下载中:" + pos);
			} catch (IOException e) {
				logger.error("接收文件信息转换失败：" + e);
				return "接收文件信息转换失败!";
			}
		}
    }

	@RequestMapping(value = "/getFile", method = RequestMethod.POST)
    public String getFile(long pos) {
		String result = null;
		Map<String, String> map = new HashMap<String, String>();
		File file = new File(dir + File.separator + READ_FILE_NAME);
		// 判断文件是否存在
		if (!file.exists() || file.length() <= 0) {
			try {
				map.put("status", "fail");
				map.put("msg", "文件不存在或文件内容为空！");
				result = objectMapper.writeValueAsString(map);
			} catch (JsonProcessingException e) {
				logger.error("json转换异常！" + e);
			}
			return result;
		}
		map.put("size", String.valueOf(file.length()));
		RandomAccessFile readFile = null;
		ByteArrayOutputStream out = null;
		try {
			readFile = new RandomAccessFile(file, "rw");
			readFile.seek(pos);
			out = new ByteArrayOutputStream();
			// 数据缓冲区, 每次读1024个字符
			byte[] buf = new byte[1024];
			if (file.length() < 1024 + pos) {
				buf = new byte[(int) (file.length() - pos)];
			}
			// 数据读写
			if (readFile.read(buf) > -1) {
				out.write(buf);
				String files = Base64.getEncoder().encodeToString(out.toByteArray());
				map.put("files", files);
				readFile.close();
				out.close();
			}
			map.put("status", "success");
		} catch (IOException e) {
			logger.error("文件读取异常！" + e);
			map.put("status", "fail");
			map.put("msg", "文件读取异常！");
		} finally {
			try {
				if (readFile != null) {
					readFile.close();
				}
				if (out != null) {
					out.close();
				}
			} catch (IOException e) {
				logger.error("文件流关闭异常！" + e);
				map.put("status", "fail");
				map.put("msg", "文件流关闭异常！");
			}
		}
		try {
			result = objectMapper.writeValueAsString(map);
		} catch (JsonProcessingException e) {
			logger.error("json转换异常！" + e);
		}
        return result;
    }
}
